package com.hf.providerexport.export;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.util.Log;

import com.hf.providerexport.database.DatabaseHelper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
 * Export Uri to database as a single table
 */
public class UriExport {
    private static final String TAG = UriExport.class.getName();

    private List<Uri> mUris = new ArrayList<>();
    private Context mContext = null;

    public interface OnProgressListener {
        void onProgress(String msg, int tableCurrent, int tableMax, int current, int max);
    }

    public UriExport(Context context, Uri uri) {
        this(context, Arrays.asList(new Uri[]{uri}));
    }

    public UriExport(Context context, Uri[] uris) {
        this(context, Arrays.asList(uris));
    }

    public UriExport(Context context, List<Uri> uris) {
        if (context == null || uris.isEmpty()) {
            throw new IllegalArgumentException("context should not be null and uris should not be empty");
        }
        mContext = context;
        mUris.addAll(uris);
    }

    private void notifyProgress(OnProgressListener listener, String msg, int tableCurrent, int tableMax, int current, int max) {
        if (listener != null) {
            listener.onProgress(msg, tableCurrent, tableMax, current, max);
        }
    }

    private static String getTableName(Uri uri) {
        return String.format(Locale.getDefault(), "[%s%s]", uri.getAuthority(), uri.getPath());
    }

    public boolean export(DatabaseHelper helper, OnProgressListener listener) {
        int nbUris = mUris.size();

        for (int i=0; i<nbUris; i++) {
            if (!exportUri(mUris.get(i), helper, listener, i, nbUris)) {
                return false;
            }
        }

        return true;
    }

    private boolean exportUri(Uri uri, DatabaseHelper helper, OnProgressListener listener, int currentUriIndex, int totalUriCount) {
        if (helper == null) {
            throw new IllegalArgumentException("helper should not be null.");
        }

        // progress message
        String msg = uri.toString();

        // generate table name
        String table = getTableName(uri);

        // do export
        notifyProgress(listener, msg, currentUriIndex, totalUriCount, 0, -1);
        try (Cursor c = mContext.getContentResolver().query(uri, null, null, null, null)) {
            if (c != null) {
                // drop old table if exists
                dropOldTable(table, helper);

                // create table
                String[] columns = c.getColumnNames();
                int[] types = new int[columns.length];
                if (c.moveToFirst()) {
                    for (int i = 0; i < types.length; i++) {
                        types[i] = c.getType(i);
                    }
                    helper.createTable(table, columns, types);
                }

                // fill data
                int current = 0;
                int max = c.getCount();
                if (c.moveToFirst()) {
                    helper.beginTransaction();
                    try {
                        do {
                            // insert values
                            ContentValues cv = new ContentValues();
                            for (int i = 0; i < columns.length; i++) {
                                fillContentValues(cv, c, i);
                            }
                            helper.insertNoTran(table, cv);

                            // notify progress
                            current++;
                            notifyProgress(listener, msg, currentUriIndex, totalUriCount, current, max);
                        } while (c.moveToNext());
                        helper.setTransactionSuccessful();
                    } finally {
                        helper.endTransaction();
                    }
                }

                // log not item in table
                if (current <= 0) {
                    Log.w(TAG, "No item found with uri: " + uri.toString());
                }
            } else {
                Log.w(TAG, "Get null cursor for table: " + table);
            }
        } catch (IllegalStateException | SQLiteException e) {
            Log.w(TAG, e.getMessage());
        }

        return true;
    }

    private static void dropOldTable(String table, DatabaseHelper helper) {
        try {
            helper.dropTable(table);
        } catch (SQLiteException e) {
            // it doesn't matter, if exception here
        }
    }

    private static void fillContentValues(ContentValues cv, Cursor cursor, int index) {
        int type = cursor.getType(index);
        String column = cursor.getColumnName(index);
        switch (type) {
            case Cursor.FIELD_TYPE_BLOB:
                cv.put(column, cursor.getBlob(index));
                break;
            case Cursor.FIELD_TYPE_FLOAT:
                cv.put(column, cursor.getFloat(index));
                break;
            case Cursor.FIELD_TYPE_INTEGER:
                cv.put(column,  cursor.getInt(index));
                break;
            case Cursor.FIELD_TYPE_NULL:
            case Cursor.FIELD_TYPE_STRING:
            default:
                cv.put(column, cursor.getString(index));
        }
    }
}
